Using External Document Type Definitions

By now you know the importance of the DTD in an XML document.  Already, you have enough knowledge about DTD’s to create rich content models.  However, there are a number of other capabilities that can be exploited within the DTD that are going to be discussed now.

Document Type Declarations

Now that you are going to always implement a DTD, let’s talk about the association of a DTD with a XML document.  This is done using a document type declaration (not to be confused with a document type definition).  You have already seen and used document type declarations, but it is important to understand the distinction between the declaration and the DTD.  The syntax that you have seen so far is:

<!DOCTYPE dtd.name [ internal.subset ]>

So far, we have created internal DTD’s.  In other words, the DTD is declared inside of the document.  All element, attribute, and entity declarations have been defined in the internal subset of the DTD.  Soon we are going to discuss external subsets and DTD’s.

Standalone Documents

A standalone document is one that does not require any external support and without reference to any other files.  In other words, the XML document stands on its own.  We can specify that our documents are standalone in the XML processing instruction.

<?xml version=”1.0” standalone=”yes”?>

The statement standalone=”yes” means that there are no markup declarations external to the document entity.  This does not mean that our document cannot reference external entities (other documents, binary files, etc.) provided that the declarations of the external entities are contained inside the document entity (inside the internal DTD subset).  This is a very important point and it needs to be stressed further.

The existence of external entities does not mean that the document is not standalone.  As long as these entities are declared within the internal subset of the DTD, the document is still considered to be a standalone document.  This is a very important point and may take a while to completely understand.  The term “standalone” does not only refer to documents that are physically self-contained.

Tip:	All that the “standalone” attribute does is make it explicit as to whether or not the document is a standalone document.

External DTD’s

Using internal DTD’s, you can create sophisticated XML documents that require no external references whatsoever.  Considering that a DTD can get rather large and complex, and the same DTD could possibly be used by multiple XML documents, wouldn’t it be nice if you could create a DTD that could be referenced by many XML documents?  Think of all the typing (or copying/pasting) that you could save. Well, you can.  It is called an external DTD.  

To an XML processor, there is only one DTD.  It consists of the internal DTD subset and the external DTD subset.  In other words, the XML processor uses both the internal and external DTD’s (if you have one) and treats them as one.  In XML, the internal DTD subset is read before the external DTD subset and so takes precedence.

Associating an external DTD is similar to creating an internal DTD in that it is done within the document type declaration (<!DOCTYPE> tag.).  The syntax is:

<!DOCTYPE name PUBLIC|SYSTEM external.pointer [internal.subset]>

Notice the new keywords PUBLIC and SYSTEM.  These are the same identifiers that we used when we declared external entities.  Let’s review them again in relation to DTD’s.

SYSTEM

A system identifier is a Universal Resource Identifier (URI) that is used to retrieve the DTD.  A URI is similar to a URL, except that URL’s are intended for network (Internet) use.  For all intents and purposes, the two terms are interchangeable.

A identifier can reference an absolute position or a relative position as is exemplified in the following code fragments:

<!-- Absolute reference -->
<!DOCTYPE doc.name SYSTEM  
”http://www.server.com/external.dtd”>

<!-- Relative reference -->
<!DOCTYPE doc.name SYSTEM “../DTDs/external.dtd”>

Most of the time you will use SYSTEM identifiers for your external DTD’s.

PUBLIC

A public identifier is the officially recorded identifier for a DTD.  Officially registered public identifiers are registered by the creator of the DTD with The International Standards Organization (ISO).  Public identifier names are assigned by the American Graphic Communication Association (CGA) under the direction of the ISO.

The syntax for a  public identifier name is:

reg.type // owner // description // language

Let’s break down all four elements of a public identifier name.

reg.type is a plus sign (+) sign if the owner is registered according to the ISO 9070 standard.  It is a minus sign (-) if it is not.  Most are not, so the minus sign (-) is more common.

owner is the creator of the identifier (individual or company).

description is a text description of the identifier.  Keep this short even though you can have a lot of text here.

language is the two character language code.

An example of a public identifier could be:

-//David Harding/My First Public Identifier//EN

When an XML processor encounters a PUBLIC identifier, it attempts to generate a location URI.  If it can’t locate the file, it uses a secondary parameter called a system literal, which you have to provide.  The following is an example of that.

<!DOCTYPE david.harding PUBLIC
	“-//David Harding//My First Public Identifier//EN”,
	“http://www.davidharding.com/public_id.dtd”>

In this example, if the public identifier cannot be located using the public identifier name, the URI is used.

Using an External DTD

Let’s rework our employees document so that it uses an external DTD.  The first thing we are going to do is create a new file called “Employees.dtd”, which will contain our external subset.  This will include the entire DTD that we have created for our employees document up to this point.

	<!ELEMENT employees ( employee+ )>

	<!ELEMENT employee (name, position, address, phone)>
	<!ATTLIST employee id ID #REQUIRED>

	<!ELEMENT name (first, last)>
	<!ELEMENT first (#PCDATA)>
	<!ELEMENT last  (#PCDATA)>

	<!ELEMENT position (#PCDATA)>

	<!ELEMENT address (street?, city?, state?, zip?)>
	<!ELEMENT street (#PCDATA)>
	<!ELEMENT city   (#PCDATA)>
	<!ELEMENT state  (#PCDATA)>
	<!ELEMENT zip    (#PCDATA)>

	<!ELEMENT phone (main, office*, fax*, mobile*, home*)>
	<!ELEMENT main   (#PCDATA)>
	<!ELEMENT office (#PCDATA)>
	<!ELEMENT fax    (#PCDATA)>
	<!ELEMENT mobile (#PCDATA)>
	<!ELEMENT home   (#PCDATA)>

File: Chapter 6\Employees.dtd

Notice that there is no document type declaration (i.e. <!DOCTYPE> tag).  There can only be one declaration in an XML document.  Remember, external XML that is included in your document is considered part of the same document.  In this case, the external and the internal DTD’s are treated the same.

Next, let’s modify the Employees.xml file so that it references the new external DTD file instead of declaring the content model internally.  The new XML document looks like this:

<?xml version="1.0" standalone=”yes”?>
<!DOCTYPE employees SYSTEM "Employees.dtd" [
<!-- Declare the internal subset here -->
]>

<employees>
    <employee id="A1234">
        <name>
            <first>John</first>
            <last>Doe</last>
        </name>
        <position>Programmer</position>
        <address>
            <street>123 Main Street</street>
            <city>Anywhere</city>
            <state>CA</state>
            <zip>92000</zip>
        </address>
        <phone>
            <main>(714) 555-1000</main>
            <fax>(714) 555-1001</fax>
        </phone>
    </employee>

    <employee id="A2345">
        <name>
            <first>Jane</first>
            <last>Doe</last>
        </name>
        <position>Systems Analyst</position>
        <address>
        </address>
        <phone>
            <main>(714) 555-2000</main>
            <mobile>(949) 555-2200</mobile>
            <home>(949) 555-2220</home>
        </phone>
    </employee>
</employees>

File: Chapter 6\Employees.xml

There are a number of advantages to using external DTD’s.  The most obvious is that you can create rich content models that can be utilized by multiple documents.  If the content model changes, you only have to change it one place.  

Be careful of multiple declarations when using external DTD’s.  When you use an external DTD, you still have the ability to declare things inside the internal subset.  If the XML processor finds a duplicate declaration, there are going to be errors.  Consider the following example.  The our Employee external DTD, the <name> element is declared as containing to child elements: <first> and <last>.

	<!ELEMENT name (first, last)>
	<!ELEMENT first (#PCDATA)>
	<!ELEMENT last  (#PCDATA)>

File: Chapter 6\Employees.dtd

What happens if we declare the <name> element again in our internal subset?  In the following example, we include the external DTD and redefine the <name> element as a #PCDATA element.

<!DOCTYPE employees SYSTEM "Employees.dtd" [
	<!ELEMENT name (#PCDATA)>
]>

File: Chapter 6\Employees.xml

Notice what happens when we open the document in XML Notepad.

Declaration error in Employees2.xml displayed in XML Notepad.

This may seen like an obvious thing, but when you have an external DTD that is a couple of hundred lines long and an internal subset that is just as descriptive, these types of things happen.  Using external DTD’s is a great thing, and it is recommended that you consider always using them, but make sure that you understand the external DTD while you are creating your internal subset.

Why Use an External DTD?

External DTD’s are a great way to maintain a consistent content model across multiple XML documents.  This is an especially useful feature when creating content models that must be standardized across an organization or department.  By using external DTD’s, you can make sure that all your XML documents remain consistent.

Code Listings

The following are the complete code listings for all of the samples used in this lesson.

Chapter 4\EmployeeMaster.xml

<?xml version="1.0"?>
<!DOCTYPE employees [
	<!ELEMENT employees ( employee+ )>

	<!ELEMENT employee (name, position, address, phone)>
	<!ATTLIST employee id ID #REQUIRED>

	<!ELEMENT name (first, last)>
	<!ELEMENT first (#PCDATA)>
	<!ELEMENT last  (#PCDATA)>

	<!ELEMENT position (#PCDATA)>

	<!ELEMENT address (street?, city?, state?, zip?)>
	<!ELEMENT street (#PCDATA)>
	<!ELEMENT city   (#PCDATA)>
	<!ELEMENT state  (#PCDATA)>
	<!ELEMENT zip    (#PCDATA)>

	<!ELEMENT phone 
		(main, office*, fax*, mobile*, home*)>
	<!ELEMENT main   (#PCDATA)>
	<!ELEMENT office (#PCDATA)>
	<!ELEMENT fax    (#PCDATA)>
	<!ELEMENT mobile (#PCDATA)>
	<!ELEMENT home   (#PCDATA)>

	<!ENTITY employee1 SYSTEM "JohnDoe.xml">
	<!ENTITY employee2 SYSTEM "JaneDoe.xml">
]>

<employees>

&employee1;
&employee2;

</employees>

Chapter 4\EmployeesWithDTD.xml

<?xml version="1.0"?>

<!DOCTYPE employees [
	<!ELEMENT employees ( employee+ )>

	<!ELEMENT employee 
		(name, position, address, phone)>
	<!ATTLIST employee id ID #REQUIRED>

	<!ELEMENT name (first, last)>
	<!ELEMENT first (#PCDATA)>
	<!ELEMENT last  (#PCDATA)>

	<!ELEMENT position (#PCDATA)>

	<!ELEMENT address (street?, city?, state?, zip?)>
	<!ELEMENT street (#PCDATA)>
	<!ELEMENT city   (#PCDATA)>
	<!ELEMENT state  (#PCDATA)>
	<!ELEMENT zip    (#PCDATA)>

	<!ELEMENT phone 
		(main, office*, fax*, mobile*, home*)>
	<!ELEMENT main   (#PCDATA)>
	<!ELEMENT office (#PCDATA)>
	<!ELEMENT fax    (#PCDATA)>
	<!ELEMENT mobile (#PCDATA)>
	<!ELEMENT home   (#PCDATA)>
]>

<employees>

    <employee id="A1234">
        <name>
            <first>John</first>
            <last>Doe</last>
        </name>
        <position>Programmer</position>
        <address>
            <street>123 Main Street</street>
            <city>Anywhere</city>
            <state>CA</state>
            <zip>92000</zip>
        </address>
        <phone>
            <main>(714) 555-1000</main>
            <fax>(714) 555-1001</fax>
        </phone>
    </employee>

    <employee id="A2345">
        <name>
            <first>Jane</first>
            <last>Doe</last>
        </name>
        <position>Systems Analyst</position>
        <address>
        </address>
        <phone>
            <main>(714) 555-2000</main>
            <mobile>(949) 555-2200</mobile>
            <home>(949) 555-2220</home>
        </phone>
    </employee>

</employees>

Chapter 4\EnumerationAttributes.xml

<?xml version="1.0"?>

<!DOCTYPE colors [

	<!ELEMENT colors ( primary+ )>

	<!ELEMENT primary EMPTY>
		<!ATTLIST primary color 
			( RED | GREEN | BLUE ) "RED">
]>

<colors>

	<primary color="RED"/>
	<primary color="GREEN"/>
	<primary color="BLUE"/>

	<!-- ERROR!	
	<primary color="YELLOW"/>
	-->

</colors>

Chapter 4\IDAttributes.xml

<?xml version="1.0"?>

<!DOCTYPE id.attribs [

	<!ELEMENT id.attribs 
		( element1*, element2*, element3* )>

	<!ELEMENT element1 EMPTY>
		<!ATTLIST element1 id ID #REQUIRED>

	<!ELEMENT element2 EMPTY>
		<!ATTLIST element2 el1.id IDREF #REQUIRED>

	<!ELEMENT element3 EMPTY>
		<!ATTLIST element3 el1.ids IDREFS #REQUIRED>
]>

<id.attribs>

	<!-- Create three <element1> elements each with 
		a unique ID -->
	<element1 id="A1234"/>
	<element1 id="B1234"/>
	<element1 id="C1234"/>

	<!-- Reference a single element -->	
	<element2 el1.id="A1234"/>

	<!-- Reference multiple elements -->
	<element3 el1.ids="A1234 B1234 C1234"/>	

</id.attribs>

Chapter 4\JohnDoe.xml

<employee id="A1234">
    <name>
        <first>John</first>
        <last>Doe</last>
    </name>
    <position>Programmer</position>
    <address>
        <street>123 Main Street</street>
        <city>Anywhere</city>
        <state>CA</state>
        <zip>92000</zip>
    </address>
    <phone>
        <main>(714) 555-1000</main>
        <fax>(714) 555-1001</fax>
    </phone>
</employee>

Chapter 4\JaneDoe.xml

<employee id="A2345">
    <name>
        <first>Jane</first>
        <last>Doe</last>
    </name>
    <position>Systems Analyst</position>
    <address>
    </address>
    <phone>
        <main>(714) 555-2000</main>
        <mobile>(949) 555-2200</mobile>
        <home>(949) 555-2220</home>
    </phone>
</employee>

Chapter 4\SpecialElementTypes.xml

<?xml version="1.0"?>

<!DOCTYPE root.element [
	<!ELEMENT empty.element EMPTY>
	<!ELEMENT unrestricted.element ANY>
]>

<root.element>

	<empty.element/>
	
	<unrestricted.element>
		<empty.element/>
		<empty.element/>
		<empty.element/>
	</unrestricted.element>

</root.element>

Chapter 4\TokenAttributes.xml

<?xml version="1.0"?>

<!DOCTYPE tokens [

	<!ELEMENT tokens ( element1+)>

	<!ELEMENT element1 ( #PCDATA )>
		<!ATTLIST element1 attr1 NMTOKEN #IMPLIED>
		<!ATTLIST element1 attr2 NMTOKENS #IMPLIED>
]>

<tokens>

	<element1 attr1="Token1" 
		  attr2="123_Token1 Token2 Token3">
		Some text here...
	</element1>

</tokens>

Chapter 5\Employees_NotWellFormed.xml

<?xml version="1.0"?>
<!DOCTYPE employes [
	<!ELEMENT employees ( employee+ )>

	<!ELEMENT employee 
		(name position, address, phone)>
	<!ATTLIST employee id ID>

	<!ELEMENT name (first, last)>
	<!ELEMENT first (#PCDATA)>
	<!ELEMENT last  (#PCDATA)>

	<!ELEMENT position (#PCDATA)>

	<!ELEMENT address 
		(street?, city?, state?, zip?)>
	<!ELEMENT street (#PCDATA)>
	<!ELEMENT city   (#PCDATA)>
	<!ELEMENT state  (#PCDATA)>
	<!ELEMENT zip    (#PCDATA)>

	<!ELEMENT phone (main, office*, fax*, mobile*, home*)>
	<!ELEMENT main   (#PCDATA)>
	<!ELEMENT office (#PCDATA)>
	<!ELEMENT fax    (#PCDATA)>
	<!ELEMENT mobile (#PCDATA)>
	<!ELEMENT home   (#PCDATA)>
]>

<employees>

    <employee id=A1234>
        <name>
            <first>John</first>
            <last>Doe</last>
        </name>
        <position>Programmer</position>
        <address>
            <street>123 Main Street</street>
            <city>Anywhere</city>
            <state>CA</state>
            <zip>92000</zip>
        </address>
        <phone>
            <main>(714) 555-1000</main>
            <fax>(714) 555-1001</fax>
        </phone>
    </employee>

    <employee id="A2345">
        <name>
            <first>Jane</first>
            <last>Doe</last>
        </name>
        <position>Systems Analyst</position>
        <address>
        </address>
        <phone>
            <main>(714) 555-2000<main>
            <mobile>(949) 555-2200</mobile>
            <home>(949) 555-2220</home>
        </phone>
    </employee>

</employees>

Chapter 5\Employees_WellFormed.xml

<?xml version="1.0"?>
<!DOCTYPE employees [
	<!ELEMENT employees ( employee+ )>

	<!ELEMENT employee (name, position, address, phone)>
	<!ATTLIST employee id ID #REQUIRED>

	<!ELEMENT name (first, last)>
	<!ELEMENT first (#PCDATA)>
	<!ELEMENT last  (#PCDATA)>

	<!ELEMENT position (#PCDATA)>

	<!ELEMENT address (street?, city?, state?, zip?)>
	<!ELEMENT street (#PCDATA)>
	<!ELEMENT city   (#PCDATA)>
	<!ELEMENT state  (#PCDATA)>
	<!ELEMENT zip    (#PCDATA)>

	<!ELEMENT phone (main, office*, fax*, mobile*, home*)>
	<!ELEMENT main   (#PCDATA)>
	<!ELEMENT office (#PCDATA)>
	<!ELEMENT fax    (#PCDATA)>
	<!ELEMENT mobile (#PCDATA)>
	<!ELEMENT home   (#PCDATA)>
]>

<employees>

    <employee id="A1234">
        <name>
            <first>John</first>
            <last>Doe</last>
        </name>
        <position>Programmer</position>
        <address>
            <street>123 Main Street</street>
            <city>Anywhere</city>
            <state>CA</state>
            <zip>92000</zip>
        </address>
        <phone>
            <main>(714) 555-1000</main>
            <fax>(714) 555-1001</fax>
        </phone>
    </employee>

    <employee id="A2345">
        <name>
            <first>Jane</first>
            <last>Doe</last>
        </name>
        <position>Systems Analyst</position>
        <address>
        </address>
        <phone>
            <main>(714) 555-2000</main>
            <mobile>(949) 555-2200</mobile>
            <home>(949) 555-2220</home>
        </phone>
    </employee>

</employees>

Chapter 5\TagError.xml

<?xml version="1.0"?>

<!DOCTYPE employees [
	<!ELEMENT employees ( employee+ )>

	<!ELEMENT employee (name, position, address, phone)>
	<!ATTLIST employee id ID #REQUIRED>

	<!ELEMENT name (first, last)>
	<!ELEMENT first (#PCDATA)>
	<!ELEMENT last  (#PCDATA)>

	<!ELEMENT position (#PCDATA)>

	<!ELEMENT address (street?, city?, state?, zip?)>
	<!ELEMENT street (#PCDATA)>
	<!ELEMENT city   (#PCDATA)>
	<!ELEMENT state  (#PCDATA)>
	<!ELEMENT zip    (#PCDATA)>

	<!ELEMENT phone 
		(main, office*, fax*, mobile*, home*)>
	<!ELEMENT main   (#PCDATA)>
	<!ELEMENT office (#PCDATA)>
	<!ELEMENT fax    (#PCDATA)>
	<!ELEMENT mobile (#PCDATA)>
	<!ELEMENT home   (#PCDATA)>
]>

<employees>

    <employee id="A1234">
        <name>
            <first>John</first>
            <last>Doe</last>
        </name>
        <position>Programmer</position>
        <address>
            <street>123 Main Street</street>
            <city>Anywhere</city>
            <state>CA</state>
            <zip>92000</zip>
        </address>
        <phone>
            <main>(714) 555-1000</main>
            <fax>(714) 555-1001</fax>
        </phone>
    </employee>

    <employee id="A2345">
        <name>
            <first>Jane</first>
            <last>Doe</last>
        </name>
        <position>Systems Analyst</position>
        <address>
        </address>
        <phone>
            <main>(714) 555-2000</main>
            <mobile>(949) 555-2200</mobile>
            <home>(949) 555-2220</home>
        </phone>
    </employee>

</employees>

Chapter 6\Employees.dtd

<!ELEMENT employees ( employee+ )>

<!ELEMENT employee 
	(name, position, address, phone)>
<!ATTLIST employee id ID #REQUIRED>

<!ELEMENT name (first, last)>
<!ELEMENT first (#PCDATA)>
<!ELEMENT last  (#PCDATA)>

<!ELEMENT position (#PCDATA)>

<!ELEMENT address (street?, city?, state?, zip?)>
<!ELEMENT street (#PCDATA)>
<!ELEMENT city   (#PCDATA)>
<!ELEMENT state  (#PCDATA)>
<!ELEMENT zip    (#PCDATA)>

<!ELEMENT phone 
	(main, office*, fax*, mobile*, home*)>
<!ELEMENT main   (#PCDATA)>
<!ELEMENT office (#PCDATA)>
<!ELEMENT fax    (#PCDATA)>
<!ELEMENT mobile (#PCDATA)>
<!ELEMENT home   (#PCDATA)>

Chapter 6\Employees.xml

<?xml version="1.0" standalone="yes"?>

<!DOCTYPE employees SYSTEM "Employees.dtd" [

	<!-- Declare the internal subset here -->

]>

<employees>

    <employee id="A1234">
        <name>
            <first>John</first>
            <last>Doe</last>
        </name>
        <position>Programmer</position>
        <address>
            <street>123 Main Street</street>
            <city>Anywhere</city>
            <state>CA</state>
            <zip>92000</zip>
        </address>
        <phone>
            <main>(714) 555-1000</main>
            <fax>(714) 555-1001</fax>
        </phone>
    </employee>

    <employee id="A2345">
        <name>
            <first>Jane</first>
            <last>Doe</last>
        </name>
        <position>Systems Analyst</position>
        <address>
        </address>
        <phone>
            <main>(714) 555-2000</main>
            <mobile>(949) 555-2200</mobile>
            <home>(949) 555-2220</home>
        </phone>
    </employee>

</employees>

The Completed Employee DTD

Now that we have covered many aspects of DTD’s, let’s go back and review the finished employee XML document.  With a complete DTD in place, we can have a content model in place that will provide a structure for all employee information that we may want to add to our document.

<?xml version="1.0"?>
<!DOCTYPE employees [
	<!ELEMENT employees ( employee+ )>

	<!ELEMENT employee (name, position, address, phone)>
	<!ATTLIST employee id ID #REQUIRED>

	<!ELEMENT name (first, last)>
	<!ELEMENT first (#PCDATA)>
	<!ELEMENT last  (#PCDATA)>

	<!ELEMENT position (#PCDATA)>

	<!ELEMENT address (street?, city?, state?, zip?)>
	<!ELEMENT street (#PCDATA)>
	<!ELEMENT city   (#PCDATA)>
	<!ELEMENT state  (#PCDATA)>
	<!ELEMENT zip    (#PCDATA)>

	<!ELEMENT phone (main, office*, fax*, mobile*, home*)>
	<!ELEMENT main   (#PCDATA)>
	<!ELEMENT office (#PCDATA)>
	<!ELEMENT fax    (#PCDATA)>
	<!ELEMENT mobile (#PCDATA)>
	<!ELEMENT home   (#PCDATA)>
]>

<employees>
    <employee id="A1234">
        <name>
            <first>John</first>
            <last>Doe</last>
        </name>
        <position>Programmer</position>
        <address>
            <street>123 Main Street</street>
            <city>Anywhere</city>
            <state>CA</state>
            <zip>92000</zip>
        </address>
        <phone>
            <main>(714) 555-1000</main>
            <fax>(714) 555-1001</fax>
        </phone>
    </employee>

    <employee id="A2345">
        <name>
            <first>Jane</first>
            <last>Doe</last>
        </name>
        <position>Systems Analyst</position>
        <address>
        </address>
        <phone>
            <main>(714) 555-2000</main>
            <mobile>(949) 555-2200</mobile>
            <home>(949) 555-2220</home>
        </phone>
    </employee>
</employees>

File Chapter 4\EmployeesWithDTD.xml